﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
using IcicleFramework.Components;
using IcicleFramework.Renderables.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace IcicleFramework.Renderables
{
    public abstract class BaseRenderable : IRenderable
    {
        /// <summary>
        /// A Dictionary that stores any and all IRenderableComponents used by this BaseRenderable.
        /// </summary>
        protected Dictionary<Type, IRenderableComponent> components;

        protected IBaseComponent parent;

        protected float layer;

        public bool Visible { get; set; }

        public float Scale { get; set; }

        public Color ColorChannel { get; set; }

        public Vector2 Offset { get; set; }

        public Vector2 Position { get; set; }

        public IBaseComponent Parent
        {
            get { return parent; }
            set
            {
                if (value != null)
                    parent = value;
            }
        }

        public virtual float Layer
        {
            get { return layer; }
            set
            {
                if (value < 0 || value > 1)
                    layer = 0.5f;
                else
                    layer = value;
            }
        }

        public virtual void Initialize()
        {
            foreach (IRenderableComponent component in components.Values)
                component.Initialize();
        }

        /// <summary>
        /// Gets or sets the SpriteEffects for this Renderable.
        /// </summary>
        public SpriteEffects Effects { get; set; }

        /// <summary>
        /// Gets or sets the origin point used for translating the position to draw this Renderable.
        /// </summary>
        public Vector2 Origin { get; set; }

        /// <summary>
        /// Gets or sets the current rotational value for this Renderable.
        /// </summary>
        public float Rotation { get; set; }

        protected BaseRenderable()
        {
            this.Origin = new Vector2(0f, 0f);
            this.Scale = 1.0f;
            this.layer = 1f;
            this.ColorChannel = Color.White;
            this.Offset = Vector2.Zero;
            this.Effects = SpriteEffects.None;
            this.Rotation = 0.0f;

            components = new Dictionary<Type, IRenderableComponent>();
        }

        protected BaseRenderable(BaseRenderable old)
        {
            foreach (KeyValuePair<Type, IRenderableComponent> component in old.components)
            {
                this.components.Add(component.Key, component.Value.DeepClone());
            }
        }

        public abstract void Draw(SpriteBatch spriteBatch);

        public abstract void Load(ContentManager content);

        public virtual void Update(GameTime gameTime)
        {
            Position = Parent.Parent.Position;

            foreach (IRenderableComponent component in components.Values)
                component.Update(gameTime);
        }

        public void AddComponent(Type baseInterfaceType, IRenderableComponent component)
        {
            if (!components.ContainsKey(baseInterfaceType))
            {
                component.Parent = this;
                components.Add(baseInterfaceType, component);
            }
        }

        public void RemoveComponent(Type baseInterfaceType)
        {
            if (components.ContainsKey(baseInterfaceType))
                components.Remove(baseInterfaceType);
        }

        /// <summary>
        /// Retrieves the component of type T if this BaseRenderable contains a component
        /// of the matching type.
        /// </summary>
        /// <typeparam name="T">The type of component to be retrieved. Use the base interface for each type of
        /// component in order to properly retrieve the component.</typeparam>
        /// <returns>The component of type T if found, null if otherwise.</returns>
        public T GetComponent<T>() where T : IRenderableComponent
        {
            T foundComponent = default(T);

            if (components.ContainsKey(typeof(T)))
            {
                foundComponent = (T)components[typeof(T)];
            }

            return foundComponent;
        }

        public abstract IRenderable DeepClone();

        public virtual void Deserialize(XElement element)
        {
            float scale = 1.0f;
            if (element.Element("scale") != null)
            {
                float.TryParse(element.Element("scale").Value, NumberStyles.Float, CultureInfo.InvariantCulture,
                                out scale);
            }
            this.Scale = scale;

            Color color = Color.White;
            if (element.Element("color") != null)
            {
                color = color.ConvertColorFromString(element.Element("color").Value);
            }
            this.ColorChannel = color;

            Vector2 offset = Vector2.Zero;
            if (element.Element("offset") != null)
            {
                offset = offset.DeserializeOffset(element.Element("offset"));
            }
            this.Offset = offset;
        }

        public abstract void Dispose();
    }
}
